// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: node.hpp 317 2009-01-12 11:10:14Z heavyzheng $
//

#ifndef ZZHENG_XML_DOM_LIBXML2_NODE_HPP_20080716__
#define ZZHENG_XML_DOM_LIBXML2_NODE_HPP_20080716__

#include <zzheng/xml/config.hpp>
#include <zzheng/xml/dom/exception.hpp>
#include <zzheng/xml/dom/types.hpp>
#include <zzheng/xml/dom/basic_node_ptr.hpp>

#include <zzheng/xml/dom/libxml2/base.hpp>
#include <zzheng/xml/dom/libxml2/libxml2_fwd.hpp>

#include <cassert>
#include <string>


namespace zzheng {
namespace xml {
namespace dom {
namespace libxml2_ {

    class document; // forward declaration.
    class element;  // TODO: how to remove this?
    class xpath;    // forward declaration.

    //! This class is the abstract base class representing an XML node. It is non-copyable.
    class node: private base {

        friend class document; // document needs pimpl() for free orphans.
        friend class element;  // TODO: element needs pimpl(), how to remove this?
        friend class xpath;

        typedef basic_node_ptr<node>       pointer;
        typedef basic_node_ptr<const node> const_pointer;

    public:

        //! Pure virtual destructor. Note: the destructor does not free the underlying libxml2 node
        //! on destruction. On the contrary, the underlying libxml2 node should firstly be freed,
        //! and the wrapper object is then destructed by the callback function.
        virtual ~node() = 0;

        //! Returns the type of this node.
        //! \return the type of this node.
        node_t type() const;

        //! Returns the content of this node. This can be either the text carried directly by this
        //! node if it's a text node or comment node; or the aggregate string of the values carried
        //! by this node child's (if it's an attribute or element). Entity references are
        //! substituted.
        //! \return the value of this child node.
        std::string content() const;

        //! Sets the content of this node. Special characters (e.g. &lt;, &gt;, &amp; etc.) will be
        //! escaped automatically.
        //!
        //! For comment or text or CData section, this function sets the text carried directly by
        //! this node. For attribute or element, this function will destroy all its child nodes,
        //! and then add a text node carrying the content as the node's new child.
        //!
        //! Note: for element node, after this function is called, all its original child nodes
        //! will be destroyed and can no longer be used.
        //!
        //! \param content  the content to set.
        void set_content(const std::string& content);

        //! Checks if this node is an orphan node. An orphan node should satisfy the following
        //! conditions:
        //! - it is not the root element of the document.
        //! - it does not have a parent node.
        //! \return true if this node is an orphan node, false otherwise.
        bool is_orphan() const;

        //! Returns the name of this node. For element or attribute, this function returns the
        //! local name of the node.
        //! \return the name of this node.
        std::string name() const;

        //! Returns a structure-based path to this node.
        //! \return a structure-based path to this node.
        std::string path() const;

        //! Returns a string representation (an XML fragment) of this node.
        //! \return a string representation (an XML fragment) of this node.
        std::string str() const;

        //! Retrieves a string representation (an XML fragment) of this node.
        //! \param str  a string to hold the result.
        void str(std::string& str) const;

        //! Returns the owner document of this node.
        //! \return the owner document of this node.
        document& doc();

        //! Const version of doc().
        const document& doc() const;

        //! Returns the parent element node.
        //! \return the parent element node.
        basic_node_ptr<element> parent();

        //! Const version of parent().
        basic_node_ptr<const element> parent() const;

    protected:

        //! Constructs an object to wrap the underlying libxml2 node object. Note: this constructor
        //! should only be called by libxml2 callback functions.
        //! \param px  the underlying libxml2 node object to wrap.
        explicit node(xmlNode* px);

        xmlNode* raw() {
            assert(raw_ != 0 && "Underlying libxml2 node should not be null.");
            return raw_;
        }

        const xmlNode* raw() const {
            assert(raw_ != 0 && "Underlying libxml2 node should not be null.");
            return raw_;
        }

        xmlNode* clone_raw(bool recursive) const;

    private:

        //! Non-implemented copy constructor.
        node(const node&);

        //! Non-implemented copy assignment.
        node& operator=(const node&);

        //! Clones a copy of this node. If it is a recursive clone, copies all attributes,
        //! namespace declarations and children when applicable. Otherwise, copies only attributes
        //! and namespace declarations when applicable. This function throws a dom_error if fail to
        //! clone the node.
        //! \param recursive  true to perform a recursive clone, false otherwise.
        //! \return the cloned node object.
        //! \throws dom_error  if fail to clone the node.
        virtual node* clone(bool recursive) const = 0;

        //! Returns a const pointer to the parent element.
        const element* parent_() const;

    private:

        xmlNode* raw_; //!< The underlying libxml2 node object.

    }; // class node

} // namespace zzheng::xml::dom::libxml2_
} // namespace zzheng::xml::dom
} // namespace zzheng::xml
} // namespace zzheng

#endif // ZZHENG_XML_DOM_LIBXML2_NODE_HPP_20080716__





